﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Qreed.Diagnostics;
using System.Diagnostics;
using System.Reflection;
using Qreed.Windows.Forms.Hook;

namespace Qreed.Windows.Forms
{
    public partial class ListBoxLogger : UserControl
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ListBoxLogger"/> class.
        /// </summary>
        public ListBoxLogger()
        {
            InitializeComponent();
            InitializeImageList();

            UpdateColumns();

            DoubleBuffered = true;
        }

        private void InitializeImageList()
        {
            // If we add icons via the designer they will get blurry ... so

            Icon[] icons = new Icon[]{
                                        new Icon(Resources.DebugIcon, imageList.ImageSize),
                                        new Icon(Resources.InfoIcon, imageList.ImageSize),
                                        new Icon(Resources.WarningIcon, imageList.ImageSize),
                                        new Icon(Resources.ErrorIcon, imageList.ImageSize)
                                     };

            foreach (Icon icon in icons)
            {
                imageList.Images.Add(icon);
                icon.Dispose();
            }
        }

        /// <summary>
        /// Gets the list view.
        /// </summary>
        /// <value>The list view.</value>
        [Category("Logging")]
        [Description("The ListView control")]
        public ListView ListView
        {
            get { return this.listView; }
        }

        private BaseLogger _logger;
        /// <summary>
        /// Gets or sets the logger.
        /// </summary>
        /// <value>The logger.</value>
        [Category("Logging")]
        [Description("The logger to use.")]
        [RefreshProperties(RefreshProperties.Repaint)]
        [DefaultValue(null)]
        public BaseLogger Logger
        {
            get{return this._logger;}
            set 
            {
                LoggerChangedEventArgs e = new LoggerChangedEventArgs(_logger, value);

                OnLoggerChange(e);
               
                this._logger = value; 
            }
        }

        private int _maxToolTipWidth = 600;
        /// <summary>
        /// Gets or sets the width of the max tool tip.
        /// </summary>
        /// <value>The width of the max tool tip.</value>
        [Category("Logging")]
        [Description("The maximum width of the tooltip in pixels.")]
        [DefaultValue(600)]
        public int MaxToolTipWidth
        {
            get { return this._maxToolTipWidth; }
            set { this._maxToolTipWidth = value; }
        }

        /// <summary>
        /// Occurs when the <see cref="Logger"/> is changed.
        /// </summary>
        public event EventHandler<LoggerChangedEventArgs> LoggerChange;

        /// <summary>
        /// Raises the <see cref="E:LoggerChange"/> event.
        /// </summary>
        /// <param name="e">The <see cref="Qreed.Windows.Forms.LoggerChangedEventArgs"/> instance containing the event data.</param>
        protected virtual void OnLoggerChange(LoggerChangedEventArgs e)
        {
            if (LoggerChange != null)
                LoggerChange(this, e);

            if (e.OldLogger != null)
            {
                e.OldLogger.WriteMessage -= new EventHandler<LoggerEventArgs>(OnLoggerWriteMessage);
                e.OldLogger.LevelChanged -= new EventHandler<LoggerLevelChange>(OnLoggerLevelChanged);
                e.OldLogger.IncludeStackFrameChanged -= new EventHandler(OnLoggerIncludeCallingFunctionNameChanged);

                if(e.OldLogger.InvokeControl == this)
                    e.OldLogger.InvokeControl = null;
            }

            if (e.NewLogger != null)
            {
                e.NewLogger.WriteMessage += new EventHandler<LoggerEventArgs>(OnLoggerWriteMessage);
                e.NewLogger.LevelChanged += new EventHandler<LoggerLevelChange>(OnLoggerLevelChanged);
                e.NewLogger.IncludeStackFrameChanged += new EventHandler(OnLoggerIncludeCallingFunctionNameChanged);
                
                if(e.NewLogger.InvokeControl == null)
                    e.NewLogger.InvokeControl = this;

                UpdateColumns();
            }
        }

        /// <summary>
        /// Called when the IncludeCallingFunctionNameChanged property changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnLoggerIncludeCallingFunctionNameChanged(object sender, EventArgs e)
        {
            UpdateColumns();

            if (DesignMode)
                CreateDesignTimeListItems();
        }

        private void UpdateColumns()
        {
            if (Logger != null)
            {
                if (Logger.IncludeStackFrame)
                {
                    if (!ListView.Columns.ContainsKey("columnFunctionName"))
                    {
                        ColumnHeader header = new ColumnHeader();
                        header.Name = "columnFunctionName";
                        header.Text = "Function";
                        header.Width = 100;

                        ListView.Columns.Add(header);
                    }
                }
                else if (ListView.Columns.ContainsKey("columnFunctionName"))
                {
                    ListView.Columns.RemoveByKey("columnFunctionName");
                }
            }
        }

        /// <summary>
        /// Called when the LogLevel has been changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event arguments.</param>
        protected virtual void OnLoggerLevelChanged(object sender, LoggerLevelChange e)
        {
            UpdateColumns();

            if (DesignMode)
                CreateDesignTimeListItems();
        }

        /// <summary>
        /// Called when logger writes a message.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Qreed.Diagnostics.LoggerEventArgs"/> instance containing the event data.</param>
        protected virtual void OnLoggerWriteMessage(object sender, LoggerEventArgs e)
        {
            ListViewItem lvItem = new ListViewItem();
            lvItem.Tag = e;

            lvItem.ImageIndex = (int)e.Level;

            lvItem.SubItems.Add(e.Message);
            lvItem.SubItems.Add(e.TimeStamp.ToString());
            lvItem.SubItems.Add((string.IsNullOrEmpty(e.Details) ? "" : e.Details.Replace(Environment.NewLine, " ")));

            if (!string.IsNullOrEmpty(e.CallingFunctionName))
                lvItem.SubItems.Add(e.CallingFunctionName);
            
            ListView.Items.Add(lvItem);

            if(ListView.SelectedIndices.Count == 0)
                ListView.EnsureVisible(lvItem.Index);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.UserControl.Load"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnLoad(EventArgs e)
        {
            if (DesignMode)
            {
                // Display some entries on design time
                CreateDesignTimeListItems();
            }
            else if (Logger == null)
            {
                throw (new ApplicationException("No logger set."));
            }

            base.OnLoad(e);
        }

        private void CreateDesignTimeListItems()
        {
            BaseLogger designTimeLogger = new BaseLogger();
            BaseLogger origLogger = Logger;

            if (origLogger != null)
            {
                designTimeLogger.IncludeStackFrame = origLogger.IncludeStackFrame;
                designTimeLogger.Level = origLogger.Level;
            }

            Logger = designTimeLogger;

            Logger.Write("A debug message has been logged.", LogLevel.Debug);
            Logger.Write("This is a information message.");
            Logger.Write("Warning message here.", LogLevel.Warning);
            Logger.Write("Oh dear! An error has occured.", new Exception("Unhandled exception while doing something stupid."));

            Logger = origLogger;
        }

       

        private int MAKELONG(int loWord, int hiWord)
        {
            return (hiWord << 16) | (loWord & 0xffff);
        }

        private void OnListViewMouseMove(object sender, MouseEventArgs e)
        {
            ListViewItem lvItem = ListView.GetItemAt(e.X, e.Y);

            if (lvItem != null)
            {
                Point pos = new Point(e.X, e.Y);

                BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty;

                object o = typeof(ToolTip).InvokeMember("Handle", flags, null, toolTip, null);
                IntPtr handle = (IntPtr)o;

                if (object.Equals(lvItem, toolTip.Tag))
                {
                    return;
                }

                toolTip.Tag = lvItem;

                LoggerEventArgs logEventArgs = (LoggerEventArgs)lvItem.Tag;

                string message = logEventArgs.Message;

                if (!string.IsNullOrEmpty(logEventArgs.Details))
                {
                    message += Environment.NewLine + logEventArgs.Details;
                }

                toolTip.Show(message, ListView, pos, 10000);

                uint TTM_SETMAXTIPWIDTH = 0x0418;

                User32.SendMessage(handle, TTM_SETMAXTIPWIDTH, 0, (uint)MaxToolTipWidth);
            }
            else
            {
                toolTip.Hide(ListView);
            }
        }

        private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ListView.SelectedItems == null || ListView.SelectedItems.Count == 0)
                return;

            StringBuilder sb = new StringBuilder();

            foreach (ListViewItem lvItem in ListView.SelectedItems)
            {
                LoggerEventArgs logEvent = (LoggerEventArgs)lvItem.Tag;

                sb.AppendLine(logEvent.ToLogString());

                /*sb.Append(logEvent.TimeStamp);
                sb.Append(" - ");
                sb.Append(logEvent.Level.ToString());
                sb.Append(" - ");

                if (string.IsNullOrEmpty(logEvent.CallingFunctionName))
                    sb.AppendLine("No stacktrace.");
                else
                    sb.AppendLine(logEvent.CallingFunctionName);

                sb.AppendLine(logEvent.Message);

                if (!string.IsNullOrEmpty(logEvent.Details))
                    sb.AppendLine(logEvent.Details);*/
            }

            Clipboard.SetText(sb.ToString());
        }

        private void selectAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (ListViewItem lvItem in ListView.Items)
                lvItem.Selected = true;
        }

        private void clearToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ListView.Items.Clear();
        }

        private void contextMenuStrip_Opening(object sender, CancelEventArgs e)
        {
            toolTip.Hide(ListView);
        }

        private void mouseLeaveTimer_Tick(object sender, EventArgs e)
        {
            // because the mouse leave event is abit buggy we use this timer
            // event to track if the mouse is still hovering our control.

            Point pt = ListView.PointToClient(MousePosition);

            if (!ListView.ClientRectangle.Contains(pt))
                toolTip.Hide(ListView);
        }


        
        
    }
}
